home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / internet / other / ka9q / ka9q_src.arc / FTPCLI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-29  |  14.0 KB  |  643 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "netuser.h"
  6. #include "icmp.h"
  7. #include "timer.h"
  8. #include "tcp.h"
  9. #include "iface.h"
  10. #include "ax25.h"
  11. #include "lapb.h"
  12. #include "ftp.h"
  13. #include "session.h"
  14. #include "cmdparse.h"
  15.  
  16. extern struct session *current;
  17. extern char nospace[];
  18. extern char badhost[];
  19. static char notsess[] = "Not an FTP session!\n";
  20. static char cantwrite[] = "Can't write %s\n";
  21. static char cantread[] = "Can't read %s\n";
  22.  
  23. int donothing(),doftpcd(),dolist(),doget(),dols(),dopass(),dopass2(),doput(),
  24.     dotype(),doabort(),domkdir(),dormdir();
  25.  
  26. struct cmds ftpabort[] = {
  27.     "abort",     doabort, 0, NULLCHAR, NULLCHAR,
  28.     NULLCHAR,    NULLFP,  0, "Only valid command is \"abort\"", NULLCHAR,
  29. };
  30.  
  31. struct cmds ftpcmds[] = {
  32.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  33.     "cd",        doftpcd,    2,    "cd <directory>",    NULLCHAR,
  34.     "dir",        dolist,        0,    NULLCHAR,        NULLCHAR,
  35.     "list",        dolist,        0,    NULLCHAR,        NULLCHAR,
  36.     "get",        doget,        2,    "get remotefile <localfile>",    NULLCHAR,
  37.     "ls",        dols,        0,    NULLCHAR,        NULLCHAR,
  38.     "mkdir",    domkdir,    2,    "mkdir <directory>",    NULLCHAR,
  39.     "nlst",        dols,        0,    NULLCHAR,        NULLCHAR,
  40.     "rmdir",    dormdir,    2,    "rmdir <directory>",    NULLCHAR,
  41.     "pass",        dopass,        1,    "pass [<password>]",    NULLCHAR,
  42.     "put",        doput,        2,    "put localfile <remotefile>",    NULLCHAR,
  43.     "type",        dotype,        0,    NULLCHAR,        NULLCHAR,
  44.     NULLCHAR,    NULLFP,        0,     NULLCHAR,        NULLCHAR,
  45. };
  46.  
  47. /* Handle top-level FTP command */
  48. doftp(argc,argv)
  49. int argc;
  50. char *argv[];
  51. {
  52.     int32 resolve();
  53.     int ftpparse();
  54.     char *inet_ntoa();
  55.     void ftpccr(),ftpccs();
  56.     struct session *s;
  57.     struct ftp *ftp,*ftp_create();
  58.     struct tcb *tcb;
  59.     struct socket lsocket,fsocket;
  60.  
  61.     lsocket.address = ip_addr;
  62.     lsocket.port = lport++;
  63.     if((fsocket.address = resolve(argv[1])) == 0){
  64.         printf(badhost,argv[1]);
  65.         return 1;
  66.     }
  67.     if(argc < 3)
  68.         fsocket.port = FTP_PORT;
  69.     else
  70.         fsocket.port = atoi(argv[2]);
  71.  
  72.     /* Allocate a session control block */
  73.     if((s = newsession()) == NULLSESSION){
  74.         printf("Too many sessions\n");
  75.         return 1;
  76.     }
  77.     current = s;
  78.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  79.         strcpy(s->name,argv[1]);
  80.     s->type = FTP;
  81.     s->parse = ftpparse;
  82.  
  83.     /* Allocate an FTP control block */
  84.     if((ftp = ftp_create(0)) == NULLFTP){
  85.         s->type = FREE;
  86.         printf(nospace);
  87.         return 1;
  88.     }
  89.     ftp->state = COMMAND_STATE;
  90.     s->cb.ftp = ftp;    /* Downward link */
  91.     ftp->session = s;    /* Upward link */
  92.  
  93.     /* Now open the control connection */
  94.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  95.         0,ftpccr,NULLVFP,ftpccs,0,(char *)ftp);
  96.     ftp->control = tcb;
  97.     go();
  98.     return 0;
  99. }
  100. /* Parse user FTP commands */
  101. int
  102. ftpparse(line,len)
  103. char *line;
  104. int16 len;
  105. {
  106.     struct mbuf *bp;
  107.  
  108.     if(current->cb.ftp->state == PASSCMD_STATE) {
  109.         (void)dopass2(line, len) ;
  110.         return ;
  111.     }
  112.  
  113.     if(current->cb.ftp->state != COMMAND_STATE){
  114.         /* The only command allowed in data transfer state is ABORT */
  115.         if(cmdparse(ftpabort,line) == -1){
  116.             printf("Transfer in progress; only ABORT is acceptable\n");
  117.         }
  118.         fflush(stdout);
  119.         return;
  120.     }
  121.  
  122.     /* Save it now because cmdparse modifies the original */
  123.     bp = qdata(line,len);
  124.  
  125.     if(cmdparse(ftpcmds,line) == -1){
  126.         /* Send it direct */
  127.         if(bp != NULLBUF)
  128.             send_tcp(current->cb.ftp->control,bp);
  129.         else
  130.             printf(nospace);
  131.     } else {
  132.         free_p(bp);
  133.     }
  134.     fflush(stdout);
  135. }
  136. /* Handle null line to avoid trapping on first command in table */
  137. static
  138. int
  139. donothing(argc,argv)
  140. int argc;
  141. char *argv[];
  142. {
  143. }
  144. /* Translate 'cd' to 'cwd' for convenience */
  145. static
  146. int
  147. doftpcd(argc,argv)
  148. int argc;
  149. char *argv[];
  150. {
  151.     register struct ftp *ftp;
  152.  
  153.     ftp = current->cb.ftp;
  154.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  155. }
  156. /* Translate 'mkdir' to 'xmkd' for convenience */
  157. static
  158. int
  159. domkdir(argc,argv)
  160. int argc;
  161. char *argv[];
  162. {
  163.     register struct ftp *ftp;
  164.  
  165.     ftp = current->cb.ftp;
  166.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  167. }
  168. /* Translate 'rmdir' to 'xrmd' for convenience */
  169. static
  170. int
  171. dormdir(argc,argv)
  172. int argc;
  173. char *argv[];
  174. {
  175.     register struct ftp *ftp;
  176.  
  177.     ftp = current->cb.ftp;
  178.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  179. }
  180. /* Handle "type" command from user */
  181. static
  182. int
  183. dotype(argc,argv)
  184. int argc;
  185. char *argv[];
  186. {
  187.     register struct ftp *ftp;
  188.  
  189.     ftp = current->cb.ftp;
  190.     if(argc < 2){
  191.         switch(ftp->type){
  192.         case IMAGE_TYPE:
  193.             printf("Image\n");
  194.             break;
  195.         case ASCII_TYPE:
  196.             printf("Ascii\n");
  197.             break;
  198.         }
  199.         return 0;
  200.     }
  201.     switch(*argv[1]){
  202.     case 'i':
  203.     case 'b':
  204.         ftp->type = IMAGE_TYPE;
  205.         sndftpmsg(ftp,"TYPE I\r\n");
  206.         break;
  207.     case 'a':
  208.         ftp->type = ASCII_TYPE;
  209.         sndftpmsg(ftp,"TYPE A\r\n");
  210.         break;
  211.     case 'l':
  212.         ftp->type = IMAGE_TYPE;
  213.         sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  214.         break;
  215.     default:
  216.         printf("Invalid type %s\n",argv[1]);
  217.         return 1;
  218.     }
  219.     return 0;
  220. }
  221. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  222. static
  223. doget(argc,argv)
  224. int argc;
  225. char *argv[];
  226. {
  227.     void ftpdr(),ftpcds();
  228.     char *remotename,*localname;
  229.     register struct ftp *ftp;
  230.     char *mode;
  231.  
  232.     ftp = current->cb.ftp;
  233.     if(ftp == NULLFTP){
  234.         printf(notsess);
  235.         return 1;
  236.     }
  237.     remotename = argv[1];
  238.     if(argc < 3)
  239.         localname = remotename;
  240.     else
  241.         localname = argv[2];
  242.  
  243.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  244.         fclose(ftp->fp);
  245.     ftp->fp = NULLFILE;
  246.  
  247.     if(ftp->type == IMAGE_TYPE)
  248.         mode = binmode[WRITE_BINARY];
  249.     else
  250.         mode = "w";
  251.  
  252.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  253.         printf(cantwrite,localname);
  254.         return 1;
  255.     }
  256.     ftp->state = RECEIVING_STATE;
  257.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  258.  
  259.     /* Generate the command to start the transfer */
  260.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  261. }
  262. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  263. static
  264. dolist(argc,argv)
  265. int argc;
  266. char *argv[];
  267. {
  268.     void ftpdr(),ftpcds();
  269.     register struct ftp *ftp;
  270.  
  271.     ftp = current->cb.ftp;
  272.     if(ftp == NULLFTP){
  273.         printf(notsess);
  274.         return 1;
  275.     }
  276.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  277.         fclose(ftp->fp);
  278.     ftp->fp = NULLFILE;
  279.  
  280.     if(argc < 3){
  281.         ftp->fp = stdout;
  282.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  283.         printf(cantwrite,argv[2]);
  284.         return 1;
  285.     }
  286.     ftp->state = RECEIVING_STATE;
  287.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  288.     /* Generate the command to start the transfer
  289.      * It's done this way to avoid confusing the 4.2 FTP server
  290.      * if there's no argument
  291.      */
  292.     if(argc > 1)
  293.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  294.     else
  295.         return sndftpmsg(ftp,"LIST\r\n","");
  296. }
  297. /* Abbreviated (name only) list of remote directory.
  298.  * Syntax: ls <remote directory/file> [<local name>]
  299.  */
  300. static
  301. dols(argc,argv)
  302. int argc;
  303. char *argv[];
  304. {
  305.     void ftpdr(),ftpcds();
  306.     register struct ftp *ftp;
  307.  
  308.     ftp = current->cb.ftp;
  309.     if(ftp == NULLFTP){
  310.         printf(notsess);
  311.         return 1;
  312.     }
  313.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  314.         fclose(ftp->fp);
  315.     ftp->fp = NULLFILE;
  316.  
  317.     if(argc < 3){
  318.         ftp->fp = stdout;
  319.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  320.         printf(cantwrite,argv[2]);
  321.         return 1;
  322.     }
  323.     ftp->state = RECEIVING_STATE;
  324.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  325.     /* Generate the command to start the transfer */
  326.     if(argc > 1)
  327.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  328.     else
  329.         return sndftpmsg(ftp,"NLST\r\n","");
  330. }
  331. /* Handle user "pass" command.  Allow Blanking of password */
  332. static
  333. int
  334. dopass(argc,argv)
  335. int argc;
  336. char *argv[];
  337. {
  338.     register struct ftp *ftp;
  339.  
  340.     ftp = current->cb.ftp;
  341.     if (argc > 1) /* Password printed in the clear - send it now */
  342.         return sndftpmsg(ftp,"PASS %s\r\n",argv[1]);
  343.     else {
  344.         ftp->state = PASSCMD_STATE ;
  345.         current->noecho = 1 ;
  346.         noecho() ;
  347.         printf("Enter user password: ") ;
  348.         fflush(stdout) ;
  349.     }
  350.     return 0;
  351. }
  352. /* Handle second half of user "pass" command: Blanked Password */
  353. static
  354. int
  355. dopass2(line,len)
  356. char *line;
  357. int16 len;
  358. {
  359.     register struct ftp *ftp;
  360.  
  361.     ftp = current->cb.ftp;
  362.     ftp->state = COMMAND_STATE ;
  363.     current->noecho = 0 ;
  364.     echo() ;
  365.  
  366.     /* Have unechoed password - send it */
  367.     line[len-1] = '\0' ;
  368.     return sndftpmsg(ftp,"PASS %s\r\n",line);
  369. }
  370. /* Start transmit. Syntax: put <local name> [<remote name>] */
  371. static
  372. doput(argc,argv)
  373. int argc;
  374. char *argv[];
  375. {
  376.     void ftpdt(),ftpcds();
  377.     char *remotename,*localname;
  378.     char *mode;
  379.     struct ftp *ftp;
  380.  
  381.     if((ftp = current->cb.ftp) == NULLFTP){
  382.         printf(notsess);
  383.         return 1;
  384.     }
  385.     localname = argv[1];
  386.     if(argc < 3)
  387.         remotename = localname;
  388.     else
  389.         remotename = argv[2];
  390.  
  391.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  392.         fclose(ftp->fp);
  393.  
  394.     if(ftp->type == IMAGE_TYPE)
  395.         mode = binmode[READ_BINARY];
  396.     else
  397.         mode = "r";
  398.  
  399.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  400.         printf(cantread,localname);
  401.         return 1;
  402.     }
  403.     ftp->state = SENDING_STATE;
  404.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  405.  
  406.     /* Generate the command to start the transfer */
  407.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  408. }
  409. /* Abort a GET or PUT operation in progress. Note: this will leave
  410.  * the partial file on the local or remote system
  411.  */
  412. doabort(argc,argv)
  413. int argc;
  414. char *argv[];
  415. {
  416.     register struct ftp *ftp;
  417.  
  418.     ftp = current->cb.ftp;
  419.  
  420.     /* Close the local file */
  421.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  422.         fclose(ftp->fp);
  423.     ftp->fp = NULLFILE;
  424.  
  425.     switch(ftp->state){
  426.     case SENDING_STATE:
  427.         /* Send a premature EOF.
  428.          * Unfortunately we can't just reset the connection
  429.          * since the remote side might end up waiting forever
  430.          * for us to send something.
  431.          */
  432.         close_tcp(ftp->data);
  433.         printf("Put aborted\n");
  434.         break;
  435.     case RECEIVING_STATE:
  436.         /* Just exterminate the data channel TCB; this will
  437.          * generate a RST on the next data packet which will
  438.          * abort the sender
  439.          */
  440.         del_tcp(ftp->data);
  441.         ftp->data = NULLTCB;
  442.         printf("Get aborted\n");
  443.         break;
  444.     }
  445.     ftp->state = COMMAND_STATE;
  446.     fflush(stdout);
  447. }
  448. /* create data port, and send PORT message */
  449. static
  450. ftpsetup(ftp,recv,send,state)
  451. struct ftp *ftp;
  452. void (*send)();
  453. void (*recv)();
  454. void (*state)();
  455. {
  456.     struct socket lsocket;
  457.     struct mbuf *bp;
  458.  
  459.     lsocket.address = ip_addr;
  460.     lsocket.port = lport++;
  461.  
  462.     /* Compose and send PORT a,a,a,a,p,p message */
  463.  
  464.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  465.         printf(nospace);
  466.         return;
  467.     }
  468.     /* I know, this looks gross, but it works! */
  469.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  470.         hibyte(hiword(lsocket.address)),
  471.         lobyte(hiword(lsocket.address)),
  472.         hibyte(loword(lsocket.address)),
  473.         lobyte(loword(lsocket.address)),
  474.         hibyte(lsocket.port),
  475.         lobyte(lsocket.port));
  476.     bp->cnt = strlen(bp->data);
  477.     send_tcp(ftp->control,bp);
  478.  
  479.     /* Post a listen on the data connection */
  480.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  481.         recv,send,state,0,(char *)ftp);
  482. }
  483. /* FTP Client Control channel Receiver upcall routine */
  484. void
  485. ftpccr(tcb,cnt)
  486. register struct tcb *tcb;
  487. int16 cnt;
  488. {
  489.     struct mbuf *bp;
  490.     struct ftp *ftp;
  491.  
  492.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  493.         /* Unknown connection; kill it */
  494.         close_tcp(tcb);
  495.         return;
  496.     }
  497.     /* Hold output if we're not the current session */
  498.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  499.         return;
  500.  
  501.     if(recv_tcp(tcb,&bp,cnt) > 0){
  502.         while(bp != NULLBUF){
  503.             fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
  504.             bp = free_mbuf(bp);
  505.         }
  506.         fflush(stdout);
  507.     }
  508. }
  509.  
  510. /* FTP Client Control channel State change upcall routine */
  511. static
  512. void
  513. ftpccs(tcb,old,new)
  514. register struct tcb *tcb;
  515. char old,new;
  516. {
  517.     void ftp_delete();
  518.     struct ftp *ftp;
  519.     char notify = 0;
  520.     extern char *tcpstates[];
  521.     extern char *reasons[];
  522.     extern char *unreach[];
  523.     extern char *exceed[];
  524.  
  525.     /* Can't add a check for unknown connection here, it would loop
  526.      * on a close upcall! We're just careful later on.
  527.      */
  528.     ftp = (struct ftp *)tcb->user;
  529.  
  530.     if(current != NULLSESSION && current->cb.ftp == ftp)
  531.         notify = 1;
  532.  
  533.     switch(new){
  534.     case CLOSE_WAIT:
  535.         if(notify)
  536.             printf("%s\n",tcpstates[new]);
  537.         close_tcp(tcb);
  538.         break;
  539.     case CLOSED:    /* heh heh */
  540.         if(notify){
  541.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  542.             if(tcb->reason == NETWORK){
  543.                 switch(tcb->type){
  544.                 case DEST_UNREACH:
  545.                     printf(": %s unreachable",unreach[tcb->code]);
  546.                     break;
  547.                 case TIME_EXCEED:
  548.                     printf(": %s time exceeded",exceed[tcb->code]);
  549.                     break;
  550.                 }
  551.             }
  552.             printf(")\n");
  553.             cmdmode();
  554.         }
  555.         del_tcp(tcb);
  556.         if(ftp != NULLFTP)
  557.             ftp_delete(ftp);
  558.         break;
  559.     default:
  560.         if(notify)
  561.             printf("%s\n",tcpstates[new]);
  562.         break;
  563.     }
  564.     if(notify)
  565.         fflush(stdout);
  566. }
  567. /* FTP Client Data channel State change upcall handler */
  568. static
  569. void
  570. ftpcds(tcb,old,new)
  571. struct tcb *tcb;
  572. char old,new;
  573. {
  574.     struct ftp *ftp;
  575.  
  576.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  577.         /* Unknown connection, kill it */
  578.         close_tcp(tcb);
  579.         return;
  580.     }
  581.     switch(new){
  582.     case FINWAIT2:
  583.     case TIME_WAIT:
  584.         if(ftp->state == SENDING_STATE){
  585.             /* We've received an ack of our FIN, so
  586.              * return to command mode
  587.              */
  588.             ftp->state = COMMAND_STATE;
  589.             if(current != NULLSESSION && current->cb.ftp == ftp){
  590.                 printf("Put complete, %lu bytes sent\n",
  591.                     tcb->snd.una - tcb->iss - 2);
  592.                 fflush(stdout);
  593.             }
  594.         }
  595.         break;        
  596.     case CLOSE_WAIT:
  597.         close_tcp(tcb);
  598.         if(ftp->state == RECEIVING_STATE){
  599.             /* End of file received on incoming file */
  600. #ifdef    CPM
  601.             if(ftp->type == ASCII_TYPE)
  602.                 putc(CTLZ,ftp->fp);
  603. #endif
  604.             if(ftp->fp != stdout)
  605.                 fclose(ftp->fp);
  606.             ftp->fp = NULLFILE;
  607.             ftp->state = COMMAND_STATE;
  608.             if(current != NULLSESSION && current->cb.ftp == ftp){
  609.                 printf("Get complete, %lu bytes received\n",
  610.                     tcb->rcv.nxt - tcb->irs - 2);
  611.                 fflush(stdout);
  612.             }
  613.         }
  614.         break;
  615.     case CLOSED:
  616.         ftp->data = NULLTCB;
  617.         del_tcp(tcb);
  618.         break;
  619.     }
  620. }
  621. /* Send a message on the control channel */
  622. /*VARARGS*/
  623. static
  624. int
  625. sndftpmsg(ftp,fmt,arg)
  626. struct ftp *ftp;
  627. char *fmt;
  628. char *arg;
  629. {
  630.     struct mbuf *bp;
  631.     int16 len;
  632.  
  633.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  634.     if((bp = alloc_mbuf(len)) == NULLBUF){
  635.         printf(nospace);
  636.         return 1;
  637.     }
  638.     sprintf(bp->data,fmt,arg);
  639.     bp->cnt = strlen(bp->data);
  640.     send_tcp(ftp->control,bp);
  641.     return 0;
  642. }
  643.